/*
 * Copyright (c) 2016, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


//*****************************************************************************
//
//! \addtogroup out_of_box
//! @{
//
//*****************************************************************************


//*****************************************************************************
// Includes
//*****************************************************************************
/* Standard includes                                                          */
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <ti/drivers/Power.h>
#include <ti/display/Display.h>
#ifdef __MSP432P401R__
#include <unistd.h>
#include <ti/drivers/power/PowerMSP432.h>
#elif CC32XX
#include <ti/drivers/power/PowerCC32XX.h>
#include <ti/devices/cc32xx/driverlib/prcm.h>
#endif

/* TI-DRIVERS Header files */
#include <ti/drivers/net/wifi/simplelink.h>
#include <Board.h>

/* POSIX Header files */
#include <pthread.h>
#include <time.h>

/* Example/Board Header files */
#include "platform.h"

//*****************************************************************************
// Defines
//*****************************************************************************
#define APPS_WDT_START_KEY          0xAE42DB15
#define HWREG(x) (*((volatile unsigned long *)(x)))
#define PRCM_WDT                  0x0000000B
#ifdef __MSP432P401R__
#define HIB_RETENTION_REGISTER      (0)
#elif CC32XX
#define HIB_RETENTION_REGISTER      (0x4402FC20)
#endif

extern void *InitTerm(void);
extern Display_Handle display;

//*****************************************************************************
// Typedefs
//*****************************************************************************
typedef struct sBootInfo
{
  uint8_t ucActiveImg;
  uint32_t ulImgStatus;
  uint32_t ulStartWdtKey;
  uint32_t ulStartWdtTime;
}sBootInfo_t;

//****************************************************************************
//                            Platform Functions
//****************************************************************************
//*****************************************************************************
//
//! \brief
//
//*****************************************************************************
void Platform_Reset()
{
#ifdef __MSP432P401R__
    MAP_ResetCtl_initiateSoftReset();
#elif CC32XX
    /* Reset the MCU in order to test the bundle */
    MAP_PRCMHibernateCycleTrigger();
#endif
}

void Platform_PrintError(char *errString, int code)
{
    Display_printf(display, 0, 0, "Error! code = %d, desc = %s", code,
            errString);
}

void Platform_TimerInit(void (*timerIntHandler)(sigval val), timer_t *timerId)
{
    sigevent sev;

    /* Create Timer */
    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_notify_function = timerIntHandler;
    timer_create(CLOCK_MONOTONIC, &sev, timerId);
}

void Platform_TimerStart(uint32_t asyncEvtTimeoutMsec, timer_t timerId, uint8_t periodic)
{
    struct itimerspec value;

    /* set the timeout */
    value.it_value.tv_sec = (asyncEvtTimeoutMsec / 1000);
    value.it_value.tv_nsec = (asyncEvtTimeoutMsec % 1000) * 1000000;

    value.it_value.tv_sec += (value.it_value.tv_nsec / 1000000000);
    value.it_value.tv_nsec = value.it_value.tv_nsec % 1000000000;

    if (periodic)
    {
        /* set as periodic timer */
        value.it_interval.tv_sec = value.it_value.tv_sec;
        value.it_interval.tv_nsec = value.it_value.tv_nsec;
    }
    else
    {
        /* set as one shot timer */
        value.it_interval.tv_sec = 0;
        value.it_interval.tv_nsec = 0;
    }

    /* kick the timer */
    timer_settime(timerId, 0, &value, NULL);
}

void Platform_TimerStop(timer_t timerId)
{
    struct itimerspec value;

    /* stop timer */
    value.it_interval.tv_sec = 0;
    value.it_interval.tv_nsec = 0;
    value.it_value.tv_sec = 0;
    value.it_value.tv_nsec = 0;
    timer_settime(timerId, 0, &value, NULL);
}

void Platform_TimerDeinit(timer_t timer)
{
    Platform_TimerStop(timer);
    timer_delete(timer);
}

void Platform_TimerInterruptClear(void)
{
    /* Do nothing... */
}

//*****************************************************************************
//
//! \brief check if we woken up from Hib or not
//!
//! \param[in]
//!
//! \return true if we woken up from hibernate else false
//!
//*****************************************************************************
bool Platform_IsWokenFromHib(void)
{
#ifdef __MSP432P401R__
    /* read from retention the cause for wakeup */
    return false;
#elif CC32XX
    if(MAP_PRCMSysResetCauseGet() == PRCM_HIB_EXIT)
    {
        return true;
    }
    else
    {
        return false;
    }
#endif
}

bool Platform_IsWokenFromReset(void)
{
    unsigned long resetCause = MAP_PRCMSysResetCauseGet();

    if (resetCause == PRCM_SOC_RESET || resetCause == PRCM_WDT_RESET
            || resetCause == PRCM_MCU_RESET || resetCause == PRCM_POWER_ON)
    {
        return true;
    }
    else
    {
        return false;
    }
}

uint32_t Platform_GetResetCause(void)
{
    return MAP_PRCMSysResetCauseGet();
}


//*****************************************************************************
//
//! \brief
//
//*****************************************************************************
int32_t Platform_CommitWdtConfig(int32_t TimeoutInSeconds)
{
#ifdef CC32XX
    int32_t lFileHandle;
    uint32_t ulToken = 0;
    sBootInfo_t sBootInfo;
    int32_t lRetVal;

    lFileHandle = sl_FsOpen((unsigned char *) "/sys/mcubootinfo.bin",
    SL_FS_CREATE | SL_FS_OVERWRITE | SL_FS_CREATE_MAX_SIZE(sizeof(sBootInfo)) |
    SL_FS_CREATE_SECURE | SL_FS_CREATE_PUBLIC_WRITE | SL_FS_CREATE_NOSIGNATURE,
                            (_u32 *) &ulToken);

    if (0 > lFileHandle)
    {
        //OTA_DBG_PRINT("OtaWatchDog: Error opening bootinfo file : %d\n\r",lFileHandle);
        return -1;
    }

    memset(&sBootInfo, 0, sizeof(sBootInfo_t));
    sBootInfo.ulStartWdtTime = 40000000 * TimeoutInSeconds; /* max 104 seconds */
    sBootInfo.ulStartWdtKey = APPS_WDT_START_KEY;
    lRetVal = sl_FsWrite(lFileHandle, 0, (uint8_t*) &sBootInfo,
                         sizeof(sBootInfo_t));
    lRetVal = sl_FsClose(lFileHandle, 0, 0, 0);
    if (0 != lRetVal)
    {
        //OTA_DBG_PRINT("OtaWatchDog: Failed to close the bootinfo file");
        return -1;
    }
#endif
    //Enable WDT - done by PRCMCC3200MCUInit
    //HWREG(0x4402E188) |= 0x00000020;
    return 0;
}

//*****************************************************************************
//
//! \brief
//
//*****************************************************************************
void Platform_CommitWdtStop()
{
#ifdef CC32XX
    MAP_PRCMPeripheralReset(PRCM_WDT);
#endif
}

//*****************************************************************************
//
//! Outputs a character string to the console
//!
//! This function
//!        1. prints the input string character by character on to the console.
//!
//! \param[in]  str - is the pointer to the string to be printed
//!
//! \return none
//!
//! \note If UART_NONPOLLING defined in than Message or UART write should be
//!       called in task/thread context only.
//
//*****************************************************************************
void Message(const char *str)
{
    Display_printf(display, 0, 0, (char *)str);
}

//*****************************************************************************
//
//! prints the formatted string on to the console
//!
//! \param[in]  format  - is a pointer to the character string specifying the
//!                       format in the following arguments need to be
//!                       interpreted.
//! \param[in]  [variable number of] arguments according to the format in the
//!             first parameters
//!
//! \return count of characters printed
//
//*****************************************************************************
int Report(const char *pcFormat, ...)
{
    int iRet = 0;
    char *pcBuff;
    char *pcTemp;
    int iSize = 256;
    va_list list;

    pcBuff = (char*) malloc(iSize);
    if (pcBuff == NULL)
    {
        return -1;
    }
    while (1)
    {
        va_start(list, pcFormat);
        iRet = vsnprintf(pcBuff, iSize, pcFormat, list);
        va_end(list);
        if ((iRet > -1) && (iRet < iSize))
        {
            break;
        }
        else
        {
            iSize *= 2;
            if ((pcTemp = realloc(pcBuff, iSize)) == NULL)
            {
                Message("Could not reallocate memory\n\r");
                iRet = -1;
                break;
            }
            else
            {
                pcBuff = pcTemp;
            }
        }
    }
    Message(pcBuff);
    free(pcBuff);

    return iRet;
}
